/*
 * Copyright (c) 2006-2023, RT-Thread Development Team
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Change Logs:
 * Date           Author       Notes
 * 2022-06-29     Rbb666       first version
 */

#include <rtthread.h>
#include <rtdevice.h>

#include "drv_gpio.h"

#define LED_PIN     GET_PIN(0, 1)

int main(void)
{
    rt_pin_mode(LED_PIN, PIN_MODE_OUTPUT);

    for (;;)
    {
        rt_pin_write(LED_PIN, PIN_HIGH);
        rt_thread_mdelay(500);
        rt_pin_write(LED_PIN, PIN_LOW);
        rt_thread_mdelay(500);
    }
}

#include <stdio.h>
#include "webclient.h"
#include "cJSON.h"
#include <sys/fcntl.h>

static const char base64_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/* encode image */
int base64_encode(unsigned char * bytes_to_encode, unsigned char *encode, int bytes_len)
{
    int i = 0, j = 0, encode_size = 0;
    unsigned char char_array_3[3];
    unsigned char char_array_4[4];

    while (bytes_len--)
    {
        char_array_3[i++] = *(bytes_to_encode++);

        if (i == 3)
        {
            char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
            char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
            char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
            char_array_4[3] = char_array_3[2] & 0x3f;

            for(i = 0; i < 4; i++)
            {
                encode[encode_size++] = base64_chars[char_array_4[i]];
            }
            i = 0;
        }
    }

    if (i)
    {
        for (j = i; j < 3; j++)
        {
            char_array_3[j] = '\0';
        }

        char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
        char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
        char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
        char_array_4[3] = char_array_3[2] & 0x3f;

        for(j = 0; (j < i + 1); j++)
        {
            encode[encode_size++] = base64_chars[char_array_4[j]];
        }

        while ((i++ < 3))
        {
            encode[encode_size++] = '=';
        }
    }

    return encode_size;
}

#ifndef POST_RESP_BUFSZ
#define POST_RESP_BUFSZ                10240
#endif

#ifndef POST_HEADER_BUFSZ
#define POST_HEADER_BUFSZ              1024
#endif

/* get token */
int get_ai_token(const char *uri, unsigned char *token)
{
    char *request = RT_NULL;
    int token_len = 0, index = 0;

    cJSON* cjson_parse = RT_NULL;
    cJSON* cjson_token = RT_NULL;

    if (webclient_request(uri, RT_NULL, RT_NULL, (unsigned char **)&request) < 0)
    {
        rt_kprintf("webclient send get request failed.");
        return -RT_ERROR;
    }

    rt_kprintf("webclient send get request by simplify request interface.\n");
    rt_kprintf("webclient get response data: \n");

    for (index = 0; index < rt_strlen(request); index++)
    {
        rt_kprintf("%c", request[index]);
    }
    rt_kprintf("\n");

    cjson_parse = cJSON_Parse(request);
    if(cjson_parse == RT_NULL)
    {
        rt_kprintf("parse fail.\n");
        goto __exit;
    }

    cjson_token = cJSON_GetObjectItem(cjson_parse, "access_token");
    if (cjson_token == RT_NULL)
    {
        rt_kprintf("get onject 'access_token' item fail.\n");
        goto __exit;
    }

    rt_kprintf("get_token: %s\n", cjson_token->valuestring);
    token_len = rt_strlen(cjson_token->valuestring);
    rt_memcpy(token, cjson_token->valuestring, token_len);

__exit:

    if (cjson_parse)
    {
        cJSON_Delete(cjson_parse);
        cjson_parse = RT_NULL;
        cjson_token = RT_NULL;
    }

    if (request)
    {
        web_free(request);
    }

    return token_len;
}

int get_ai_result(const char *uri, const char *post_data, int post_data_size)
{
    struct webclient_session* session = RT_NULL;
    unsigned char *buffer = RT_NULL;
    int index, result = 0, resp_status, bytes_read;

    buffer = (unsigned char *)web_malloc(POST_RESP_BUFSZ);
    if (buffer == RT_NULL)
    {
        rt_kprintf("no memory for receive response buffer.\n");
        result = -RT_ENOMEM;
        goto __exit;
    }

    /* create webclient session and set header response size */
    session = webclient_session_create(POST_HEADER_BUFSZ);
    if (session == RT_NULL)
    {
        result = -RT_ENOMEM;
        goto __exit;
    }

    /*  add http header */
    webclient_header_fields_add(session, "Content-Length: %d\r\n", post_data_size);
    webclient_header_fields_add(session, "Content-Type: application/x-www-form-urlencoded\r\n");

    /* send POST request by default header */
    if ((resp_status = webclient_post(session, uri, (const char *)post_data)) != 200)
    {
        rt_kprintf("webclient POST request failed, response(%d) error.\n", resp_status);
        result = -RT_ERROR;
        goto __exit;
    }

    rt_kprintf("webclient post response data: \n");
    do
    {
        bytes_read = webclient_read(session, buffer, POST_RESP_BUFSZ);
        if (bytes_read <= 0)
        {
            break;
        }
        for (index = 0; index < bytes_read; index++)
        {
            rt_kprintf("%c", buffer[index]);
        }

    } while (1);

    rt_kprintf("\n");

__exit:
    if (session)
    {
        webclient_close(session);
    }

    if (buffer)
    {
        web_free(buffer);
    }

    return result;
}

int http_percentage_coding(unsigned char *org_data, unsigned char *new_data, int len)
{
    int i = 0;
    unsigned char org_char = 0;

    while (len--)
    {
        org_char = *(org_data++);
        switch (org_char)
        {
        case ':' :
            new_data[i++] = '%';
            new_data[i++] = '3';
            new_data[i++] = 'A';
        break;

        case '/' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = 'F';
        break;

        case '?' :
            new_data[i++] = '%';
            new_data[i++] = '3';
            new_data[i++] = 'F';
        break;

        case '#' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '3';
        break;

        case '[' :
            new_data[i++] = '%';
            new_data[i++] = '5';
            new_data[i++] = 'B';
        break;

        case ']' :
            new_data[i++] = '%';
            new_data[i++] = '5';
            new_data[i++] = 'D';
        break;

        case '@' :
            new_data[i++] = '%';
            new_data[i++] = '4';
            new_data[i++] = '0';
        break;

        case '!' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '1';
        break;

        case '$' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '4';
        break;

        case '&' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '6';
        break;

        case '\'' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '7';
        break;

        case '(' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '8';
        break;

        case ')' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '9';
        break;

        case '*' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = 'A';
        break;

        case '+' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = 'B';
        break;

        case ',' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = 'C';
        break;

        case ';' :
            new_data[i++] = '%';
            new_data[i++] = '3';
            new_data[i++] = 'B';
        break;

        case '=' :
            new_data[i++] = '%';
            new_data[i++] = '3';
            new_data[i++] = 'D';
        break;

        case '%' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '5';
        break;

        case ' ' :
            new_data[i++] = '%';
            new_data[i++] = '2';
            new_data[i++] = '0';
        break;

        default:
            new_data[i++] = org_char;
        break;
        }
    }
    return i;
}

#define MAX_ENCODE_IMAGE_SIZE  (4096 * 4)
#define MAX_IMAGE_SIZE         (4096 * 4)

const char *access_token = "https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=[API Key]&client_secret=[Secret Key]";

const char *BAIDU_AI_API[] = {
    "https://aip.baidubce.com/rest/2.0/image-classify/v2/advanced_general", /* advanced General Url */
    "https://aip.baidubce.com/rest/2.0/image-classify/v2/dish",             /* dish Detect Url*/
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/car",              /* car Detect Url */
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/vehicle_detect",   /* vehicle Detect Url */
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/vehicle_damage",
    "https://aip.baidubce.com/rest/2.0/image-classify/v2/logo",
    "https://aip.baidubce.com/rest/2.0/realtime_search/v1/logo/add",
    "https://aip.baidubce.com/rest/2.0/realtime_search/v1/logo/delete",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/animal",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/plant",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/object_detect",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/landmark",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/flower",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/classify/ingredient",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/redwine",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/currency",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/dish/add",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/dish/search",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/realtime_search/dish/delete",
    "https://aip.baidubce.com/rest/2.0/image-classify/v1/multi_object_detect",
    "https://aip.baidubce.com/api/v1/solution/direct/imagerecognition/combination"
};

rt_err_t baidu_ai_sample(char *filename)
{
    unsigned char *ai_token = RT_NULL;
    int ai_token_len = 0;
    rt_err_t result = RT_EOK;
    int fd;
    unsigned char *buff_ptr = RT_NULL;
    unsigned char *encode_ptr = RT_NULL;

    rt_size_t total_length, encode_size, post_uri_size;

    rt_uint8_t *post_uri = RT_NULL;

    const char image_arg[] = "image=";
    rt_uint8_t image_arg_len = rt_strlen(image_arg);
    const char token_arg[] = "?access_token=";
    rt_uint8_t token_arg_len = rt_strlen(token_arg);
#ifdef BD_AI_BAIKE
    const char baike_args[] = "&baike_num=5";
    rt_uint8_t baike_args_len = rt_strlen(baike_args);
#endif

    rt_uint8_t *post_data = RT_NULL;
    rt_size_t post_data_size = 0, percent_len = 0;

    static int index = 0;

    /* step1: get token */
    ai_token = rt_malloc(256);
    if (ai_token == RT_NULL)
    {
        result = -RT_ENOMEM;
        goto __exit;
    }
    rt_kprintf("start to get baidu ai token");
    ai_token_len = get_ai_token(access_token, ai_token);
    rt_kprintf("token : %s", ai_token);
    if (ai_token_len <= 0)
    {
        result = -RT_ENOMEM;
        goto __exit;
    }
    rt_kprintf("get baidu ai token success");

    /* step2: get picture data */
    rt_kprintf("open filename : %s", filename);

    fd = open(filename, 0, O_RDONLY);
    if (fd < 0)
    {
        rt_kprintf("open file: %s failed\n", filename);
        result = -RT_ENOMEM;
        goto __exit;
    }

    buff_ptr = rt_malloc(MAX_IMAGE_SIZE);
    if (buff_ptr == RT_NULL)
    {
        rt_kprintf("no memory\n");
        result = -RT_ENOMEM;
        close(fd);
        goto __exit;
    }

    total_length = read(fd, buff_ptr, MAX_IMAGE_SIZE);
    if (total_length > MAX_IMAGE_SIZE)
    {
        rt_kprintf("image size too big.\n");
        result = -RT_ERROR;
        goto __exit;
    }
    rt_kprintf("open file success.");
    /* close file and release memory */
    close(fd);

    /* step3: encode image */
    rt_kprintf("encode image start.");
    encode_ptr = rt_malloc(MAX_ENCODE_IMAGE_SIZE);
    encode_size = base64_encode(buff_ptr, encode_ptr, total_length);

    /* step4: get post uri */
    index = strlen(BAIDU_AI_API[8]);
    post_uri_size = index;
    post_uri = rt_malloc(256);

    rt_memcpy(post_uri, BAIDU_AI_API[8], post_uri_size);
    rt_memcpy(&post_uri[post_uri_size], token_arg, token_arg_len);
    post_uri_size += token_arg_len;
    rt_memcpy(&post_uri[post_uri_size], ai_token, ai_token_len);
    post_uri_size += ai_token_len;

    rt_kprintf("post uri: %s", post_uri);

    /* step5: percentage code post data */
#ifdef BD_AI_BAIKE
    post_data = rt_malloc(encode_size * 3 + image_arg_len + baike_args_len);
#else
    post_data = rt_malloc(encode_size * 3 + image_arg_len);
#endif

    if (post_data == RT_NULL)
    {
        rt_kprintf("no memory.\n");
        result = -RT_ERROR;
        goto __exit;
    }
    rt_memcpy(post_data, image_arg, image_arg_len);

    rt_kprintf("percentage http data.");
    percent_len = http_percentage_coding(encode_ptr, &post_data[image_arg_len], encode_size);

    post_data_size = percent_len + image_arg_len;

#ifdef BD_AI_BAIKE
    if (percent_len < encode_size * 3)
    {
        rt_memcpy(&post_data[post_data_size], baike_args, baike_args_len);
        post_data_size += baike_args_len;
    }
#endif
    rt_kprintf("post data: %s", post_data);
    /* step: upload image to baidu ai */
    get_ai_result((const char *)post_uri, (const char *)post_data, post_data_size);

__exit:

    if(ai_token)
    {
        rt_free(ai_token);
    }

    if (buff_ptr)
    {
        rt_free(buff_ptr);
    }

    if (encode_ptr)
    {
        rt_free(encode_ptr);
    }

    if (post_data)
    {
        rt_free(post_data);
    }

    return result;

}

static int baidu_ai(int argc, char **argv)
{
    if (argc == 2)
    {
        baidu_ai_sample(argv[1]);
    }
    else
    {
        rt_kprintf("baidu_ai xxx.jpg\n");
    }

    return RT_EOK;
}
MSH_CMD_EXPORT(baidu_ai, baidu_ai sample);


#ifdef RT_USING_DFS
#include <dfs_fs.h>
#include "dfs_romfs.h"

int mnt_init(void)
{
    if (dfs_mount(RT_NULL, "/", "rom", 0, &(romfs_root)) == 0)
    {
        rt_kprintf("ROM file system initializated!\n");
    }
    else
    {
        rt_kprintf("ROM file system initializate failed!\n");
    }

    return 0;
}
INIT_ENV_EXPORT(mnt_init);
#endif


